package com.neusoft.neuiotms.dcvm.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import com.google.common.collect.Lists;
import com.neusoft.bizcore.auth.common.bean.RoleBean;
import com.neusoft.bizcore.auth.common.bean.UserBean;
import com.neusoft.bizcore.cache.caching.CacheService;
import com.neusoft.bizcore.web.dto.result.ResultListDTO;
import com.neusoft.bizcore.web.exception.UnitedException;
import com.neusoft.bizcore.web.utils.JsonUtils;
import com.neusoft.neuiotms.dcvm.bean.LocationCiBean;
import com.neusoft.neuiotms.dcvm.bean.LocationCiTreeBean;
import com.neusoft.neuiotms.dcvm.service.LocationService;
import com.neusoft.neuiotms.dcvm.utils.CmdbApi;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class LocationServiceImpl implements LocationService {

    private final static String ROOT_LOCATION_CODE = "IDC";

    @Autowired
    private RestTemplate restTemplate;
    @Value("${cmdb.proxy.target-url}")
    private String cmdbServiceUrl;

    @Autowired
    private CacheService cacheService;

    @SuppressWarnings("unchecked")
    @Override
    public Map<String, Object> getRootLocation(final HttpServletRequest request) {
        final ResultListDTO<Map<String, Object>> result =
                this.restTemplate.getForObject(this.cmdbServiceUrl + CmdbApi.locationTreeUrl, ResultListDTO.class);

        final List<Map<String, Object>> data = result.getData();
        if (data.size() > 0) {
            final Map<String, Object> item = data.get(0);
            final Map<String, Object> res = new HashMap<>();
            res.put("id", item.get("_id"));
            res.put("code", item.get("code"));
            res.put("name", item.get("name"));
            res.put("attrs", item.get("attrs"));
            return res;
        } else {
            throw new UnitedException("CMDB未查到任何位置CI项");
        }

    }

    @Override
    public List<LocationCiBean> getDeepLocationsByOne(final String pLocCode, final int deep) {

        final LocationCiTreeBean tree = this.getLocationCiTree(pLocCode);

        final List<LocationCiBean> list = new ArrayList<>();
        this.flatLocations(list, tree, "", deep);
        return list;
    }

    /**
     * 查找指定节点子树
     *
     * @param node 位置树
     * @param code 指定位置树节点编码
     * @return 指定子树
     */
    private LocationCiTreeBean getSpecialNode(final LocationCiTreeBean node, final String code) {
        if (code.equals(node.getCode())) {
            return node;
        } else {
            for (final LocationCiTreeBean child : node.getChildren()) {
                final LocationCiTreeBean b = this.getSpecialNode(child, code);
                if (null != b) {
                    return b;
                }
            }
        }
        return null;
    }

    /**
     * 迭代扁平化位置
     *
     * @param list 结果
     * @param node 开始节点
     * @param pCode 父节点编码
     * @param deep 深度
     */
    private void flatLocations(final List<LocationCiBean> list, final LocationCiTreeBean node, final String pCode,
            final int deep) {
        if (deep < 0) {
            return;
        }
        final LocationCiBean ci = new LocationCiBean();
        ci.set_id(node.get_id());
        ci.setCode(node.getCode());
        ci.setName(node.getName());
        ci.setAttrs(node.getAttrs());
        ci.setParent(pCode);
        list.add(ci);

        for (final LocationCiTreeBean child : node.getChildren()) {
            this.flatLocations(list, child, ci.getCode(), deep - 1);
        }

    }

    @Override
    public LocationCiTreeBean getLocationsTree(final String pLocCode) {
        return this.getLocationCiTree(pLocCode);
    }

    @SuppressWarnings("unchecked")
    public LocationCiTreeBean getLocationCiTree(final String pLocCode) {
        final boolean cached = this.cacheService.isInCache("LocationCiTree", pLocCode);
        if (cached) {
            final Object data = this.cacheService.get("LocationCiTree", pLocCode);
            return (LocationCiTreeBean) data;
        }

        List<LocationCiTreeBean> data = null;
        final boolean idcTreeCached = this.cacheService.isInCache("IdcLocationTree", "IDC");
        if (idcTreeCached) {
            data = (List<LocationCiTreeBean>) this.cacheService.get("IdcLocationTree", "IDC");
        } else {
            final ParameterizedTypeReference<ResultListDTO<LocationCiTreeBean>> typeRef =
                    new ParameterizedTypeReference<ResultListDTO<LocationCiTreeBean>>() {
                    };
            final ResponseEntity<ResultListDTO<LocationCiTreeBean>> response = this.restTemplate
                    .exchange(this.cmdbServiceUrl + CmdbApi.locationTreeUrl, HttpMethod.GET, null, typeRef);
            if (null == response) {
                throw new UnitedException("CMDB未查到指定位置CI项");
            }

            final ResultListDTO<LocationCiTreeBean> result = response.getBody();

            data = result.getData();
            this.cacheService.put("IdcLocationTree", "IDC", data);
        }

        if (data.size() > 0) {
            final LocationCiTreeBean tree = data.get(0);
            final LocationCiTreeBean specialNode = this.getSpecialNode(tree, pLocCode);
            if (null == specialNode) {
                throw new UnitedException("CMDB未查到指定位置CI项");
            }
            this.cacheService.put("LocationCiTree", pLocCode, specialNode);
            return specialNode;
        } else {
            throw new UnitedException("CMDB未查到指定位置CI项");
        }
    }

    @Override
    public List<LocationCiBean> getMyLocationsTree(UserBean user) {
        if (LocationServiceImpl.log.isDebugEnabled()) {
            LocationServiceImpl.log.debug("current user is: {}", JsonUtils.pojoToJson(user));
        }
        final List<RoleBean> roles =
                user.getRoles().stream().filter(it -> !"admin".equals(it.getRole())).collect(Collectors.toList());
        if (roles.size() == 0) {
            return Lists.newArrayList();
        }
        final RoleBean defaultRole = roles.get(0);
        final String defaultRoleCode = defaultRole.getRole();
        final LocationCiTreeBean locCiTreeBean = this.getLocationsTree(LocationServiceImpl.ROOT_LOCATION_CODE);
        if (!LocationServiceImpl.ROOT_LOCATION_CODE.equals(defaultRoleCode)) {
            final List<LocationCiTreeBean> children = locCiTreeBean.getChildren();
            final List<LocationCiTreeBean> filterdChildren =
                    children.stream().filter(c -> c.getCode().equals(defaultRoleCode)).collect(Collectors.toList());
            children.clear();
            children.addAll(filterdChildren);

        }

        final List<LocationCiBean> list = new ArrayList<>();
        this.flatLocations(list, locCiTreeBean, "", 100);

        //        return Lists.newArrayList(locCiTreeBean);
        return list;
    }

}
